﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Is_NormalResInfo
{
	public int weights_value = 0;
}
public class Is_SpecialResInfo
{
	public string type = "";
	public List<Combination> combinations = new List<Combination>();
}
public struct Combination
{
	public Card[] Top;
	public Card[] Mid;
	public Card[] Btm;
	public Combination(Card[] top, Card[] mid, Card[] btm)
	{
		this.Top = top;
		this.Mid = mid;
		this.Btm = btm;
	}
	public Combination(Card[] card_list)
	{
		this.Top = card_list.Take(3).ToArray();
		this.Mid = card_list.Skip(3).Take(5).ToArray();
		this.Btm = card_list.Skip(8).Take(5).ToArray();
	}
	public string toReadAble()
	{
		return "头:\tTOP \n中:\tMID \n尾:\tBTM "
			.Replace("TOP", Top.Select((Card card) => card.ToString()).Aggregate((res, cur_card_str) => res + ",\t" + cur_card_str))
			.Replace("MID", Mid.Select((Card card) => card.ToString()).Aggregate((res, cur_card_str) => res + ",\t" + cur_card_str))
			.Replace("BTM", Btm.Select((Card card) => card.ToString()).Aggregate((res, cur_card_str) => res + ",\t" + cur_card_str));
	}
	public object toJsonAble()
	{
		return new
		{
			Top = Top.Select((Card card) => card.ToString()),
			Mid = Mid.Select((Card card) => card.ToString()),
			Btm = Btm.Select((Card card) => card.ToString()),
		};
	}
}
public class Rule
{
	static public int POW_15_0 = (int)Math.Pow(15, 0);
	static public int POW_15_1 = (int)Math.Pow(15, 1);
	static public int POW_15_2 = (int)Math.Pow(15, 2);
	static public int POW_15_3 = (int)Math.Pow(15, 3);
	static public int POW_15_4 = (int)Math.Pow(15, 4);
	static public int POW_15_5 = (int)Math.Pow(15, 5);
	static public int POW_15_6 = (int)Math.Pow(15, 6);
	static public int POW_15_7 = (int)Math.Pow(15, 7);

	static public int[] POW_15 = new int[] { POW_15_0, POW_15_1, POW_15_2, POW_15_3, POW_15_4, POW_15_5, POW_15_6, POW_15_7 };

	/// <summary>
	/// 获取顺子的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="s_len">顺子的长度，默认为5</param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckStraight(CardList card_list, int s_len = 5)
	{
		var cards = new string[] { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A" };
		// 反转序列，从权重最大的顺子开始，方便在IsStraight能一次性获得权重最大的顺子
		var enable_number_heads = new string[] { "10", "A", "9", "8", "7", "6", "5", "4", "3", "2" };
		var hasLaizi = card_list.HasLaiziCard();
		foreach (var first_number in enable_number_heads)
		{
			if (hasLaizi || card_list.HasCardByNumber(first_number))
			{

				var baseIndex = Array.IndexOf(cards, first_number);

				var straight = cards.Skip(baseIndex).Take(s_len).ToArray();
				if (card_list.HasCardsByNumber(straight))
				{
					var indexs = new int[s_len];
					var loop = true;
					while (loop)
					{
						var straight_cards = new List<Card>();
						var max_indexs = new int[s_len];
						for (int i = 0; i < s_len; i++)
						{
							var number = straight[i];
							var selectable_cards = card_list.GetCardsByNumber(number);
							max_indexs[i] = selectable_cards.Count - 1;
							if (indexs[i] > max_indexs[i])// 有可能是癞子牌被用了
							{
								goto ZHENGLI;
							}
							var selected_card = selectable_cards.ElementAt(indexs[i]);

							// 添加到结果组中并禁用这张牌
							card_list.DisableCard(selected_card);
							straight_cards.Add(selected_card);
							if (straight_cards.Count == s_len)// 满了，直接返回
							{
								yield return straight_cards.ToArray();
								straight_cards.Clear();
								indexs[i] += 1;// 组合指向下一个
								goto ZHENGLI;
							}
						}
						// 整理并进位
						ZHENGLI:
						for (int j = s_len - 1; j >= 0; j--)
						{
							var _cur_index = indexs[j];
							var _max_index = max_indexs[j];
							if (_cur_index > _max_index)
							{
								if (j == 0)
								{// 无法再进位了，结束循环
									loop = false;
								}
								else
								{
									indexs[j] = 0;
									indexs[j - 1] += 1;
								}
							}
						}
						// 解除禁用
						card_list.ClearDisabledCards();
					}
					// 进行排列组合或许可能的组合情况并返回

				}
			}
		}
	}
	public bool IsStraight(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in this.CheckStraight(card_list))
			{
				// 得到的组合都是按顺序的
				// 其中A开头的是A,2,3,4,5，这个组合是第二大的顺子，所以不能把A当成1或者14

				int weights_value = 0;
				var i = 0;

				// 推测取得完整的卡牌组合（推测出癞子的角色）
				// 这里不可能存在五张癞子牌，如果是全癞子，那么肯定会被当成五炸
				var combination_valueOf = new int[combination.Length];
				foreach (var card in combination)
				{
					// 如果是癞子，使用下一张进行推测
					if (!card.IsAny)
					{
						var card_val = card.Weight;
						for (int j = 0; j < combination.Length; j++)
							combination_valueOf[j] = card_val - (i - j);
						//weights_value = card_val - i; 
						break;
					}
					i += 1;
				}
				// 这里计算权重是通过合并所有卡牌的权重来取得，
				// 这不是已知的最佳算法（只要推算出第一张牌是是什么牌就知道大小了）
				// 这里为了确保灵活性需求，比如癞子的特殊算法，所以有所保留，使用了合并每一张牌的牌面数值来作为最终权重
				weights_value = combination_valueOf.Aggregate(0, (pre_val, card_val) =>
					{
						if (card_val == 14 || card_val == 1)//可能癞子被推测出是1
						{
							// A
							pre_val += 100;
						}
						else
						{
							pre_val += card_val;
						}
						return pre_val;
					});

				// CheckStraight返回的顺子组合的权重是从大到小，使用只取权重最大的组合
				res_info.weights_value = weights_value;
				break;
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	// 获取一组数组的组合可能，返回数组索引表
	public static List<int[]> GetListComIndexs(int list_len, int s_len)
	{
		var res_list = new List<int[]>();
		var s_len_1 = s_len - 1;

		var indexs = new int[s_len];
		var max_index = list_len - s_len;
		var keep_loop = true;
		do
		{
			// 把当前位的组合给整理出来
			var cur_index = indexs[s_len_1];
			for (var i = cur_index; i <= max_index; i += 1)
			{
				indexs[s_len_1] = i;
				var res_indexs = new int[s_len];
				for (var j = 0; j < s_len; j += 1)
				{
					res_indexs[j] = indexs[j] + j;
				}
				res_list.Add(res_indexs);
			}
			indexs[s_len_1 - 1]++; // 进位
			keep_loop = Rule.FixIndexs(indexs, max_index);
			// 进位修正
			int z, z_len, cur_index_val = 0;
			for (z = 0, z_len = s_len - 1; z < z_len; z += 1)
			{
				cur_index_val = indexs[z];
				if (cur_index_val > indexs[z + 1])
				{
					indexs[z + 1] = cur_index_val;
				}
			}
			indexs[z] = cur_index_val;
		} while (keep_loop);

		return res_list;
	}
	public static bool FixIndexs(int[] indexs, int max_index)
	{
		var _decimal = max_index + 1;
		for (var i = indexs.Length - 1; i >= 0; i -= 1)
		{
			var cur_index = indexs[i];
			if (cur_index > max_index)
			{
				if (i == 0)
				{
					return false;
				}
				indexs[i] %= _decimal;
				indexs[i - 1] += (cur_index / _decimal) | 0;
			}
		}
		return true;
	}
	/// <summary>
	/// 获取同花的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="s_len">同花的长度，默认为5</param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckFlush(CardList card_list, int s_len = 5)
	{
		var types = new string[] { "黑桃", "红心", "梅花", "方块" };
		foreach (var type in types)
		{
			var type_cards = card_list.GetCardsByType(type);
			if (type_cards.Count == s_len)
			{
				yield return type_cards.ToArray();
			}
			else if (type_cards.Count > s_len)
			{
				var Indexs_List = Rule.GetListComIndexs(type_cards.Count, s_len);
				foreach (var indexs in Indexs_List)
				{
					var flush_cards = new Card[s_len];
					for (int i = 0; i < s_len; i++)
					{
						flush_cards[i] = type_cards.ElementAt(indexs[i]);
					}
					yield return flush_cards;
				}
			}
		}
	}
	public bool IsFlush(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in this.CheckFlush(card_list))// 这里有且只有一个返回
			{
				/* 癞子+对子 = 三张， 还有葫芦之类的，暂时不搞 */
				//if (IsThree(card_list, out res_info))
				//{
				//    res_info.weights_value *= POW_15_3;
				//} else
				// 如果有两对，权重进两位
				if (IsTwoPair(card_list, out res_info))
				{
					res_info.weights_value *= POW_15_2;
				}
				// 如果有对子，权重进一位
				else if (IsTwo(card_list, out res_info))
				{
					res_info.weights_value *= POW_15_1;
				}
				else
				{
					var i = -1;
					// 从小到大排序
					res_info.weights_value += combination.OrderBy(card => card.Weight).Aggregate(0, (pre_val, card) =>
						{
							i += 1;
							var card_weight = card.Weight;
							if (card.IsAny)//癞子
							{
								card_weight = 14;// 当成A来用
							}
							return pre_val + card_weight * POW_15[i];
						});
				}
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	/// <summary>
	/// 获取同花顺的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="s_len"></param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckStraightFlush(CardList card_list, int s_len = 5)
	{
		// 先取顺子再判断同花，这样确保返回的同花顺的权重是从大到小的
		foreach (var straight_cards in CheckStraight(card_list, s_len))
		{
			Is_NormalResInfo _;
			if (IsFlush(new CardList(straight_cards), out _))
			{
				yield return straight_cards;
			}
		}
	}
	public bool IsStraightFlush(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in this.CheckStraightFlush(card_list))
			{
				// 算法类似同花顺，但这里采用更简单的方式
				var i = 0;

				// 推测取得完整的卡牌组合（推测出癞子的角色）
				// 这里不可能存在五张癞子牌，如果是全癞子，那么肯定会被当成五炸
				var combination_valueOf = new int[combination.Length];
				foreach (var card in combination)
				{
					// 如果是癞子，使用下一张进行推测
					if (!card.IsAny)
					{
						res_info.weights_value = card.Weight - i;
						if (res_info.weights_value == 1)// 推算出是A
						{
							res_info.weights_value = 14;
						}
						break;
					}
					i += 1;
				}
				break;
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	IEnumerable<Card[]> CheckSameNumber(CardList card_list, int s_len)
	{
		var laizi_cards = card_list.GetLaiziCards();
		// 全癞子，只能返回一次，等于只能当成A
		var full_laizi = laizi_cards.Count == s_len;
		// 是否已经返回过一次全癞子
		var returned_full_laizi = false;
		// 使用权重排序，确保是先返回权重最大的
		var numbers = new string[] { "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3", "2" };
		foreach (var number in numbers)
		{
			var number_cards = card_list.GetCardsByNumber(number);
			if (number_cards.Count == s_len)
			{
				yield return number_cards.ToArray();
			}
			else if (number_cards.Count > s_len)
			{
				var Indexs_List = Rule.GetListComIndexs(number_cards.Count, s_len);
				foreach (var indexs in Indexs_List)
				{
					var same_cards = new Card[s_len];
					for (int i = 0; i < s_len; i++)
					{
						same_cards[i] = number_cards.ElementAt(indexs[i]);
					}
					if (full_laizi)
					{
						if (same_cards.All(card => card.IsAny))// 全癞子
						{
							if (!returned_full_laizi)
							{// 第一次可以返回，以后就不行了
								returned_full_laizi = true;
								yield return same_cards;
							}
						}
						else
						{
							yield return same_cards;
						}
					}
					else
					{
						yield return same_cards;
					}
				}
			}
		}
	}
	/// <summary>
	/// 获取五炸弹（癞子替代或者加色）的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckFive(CardList card_list)
	{
		return CheckSameNumber(card_list, 5);
	}
	/// <summary>
	/// 获取铁支（炸弹）的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckFour(CardList card_list)
	{
		return CheckSameNumber(card_list, 4);
	}
	/// <summary>
	/// 获取三条的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckThree(CardList card_list)
	{
		return CheckSameNumber(card_list, 3);
	}
	/// <summary>
	/// 获取对子的组合可能
	/// </summary>
	/// <param name="card_list"></param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckTwo(CardList card_list)
	{
		return CheckSameNumber(card_list, 2);
	}
	public bool IsFive(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in CheckFive(card_list))
			{
				foreach (var card in combination)
				{
					if (!card.IsAny)
					{
						res_info.weights_value = card.Weight;
						break;
					}
				}
				// 遇到五个癞子了，当成A炸
				if (res_info.weights_value == 0)
				{
					res_info.weights_value = 14;//Card.GetIns("红心_A").Weight;
				}
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	public bool IsFour(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in CheckFour(card_list))
			{
				var rest_card = CardList.FilterRemoveCards(card_list, combination)[0];
				int base_weight = 0;
				foreach (var card in combination)
				{
					if (!card.IsAny)
					{
						base_weight = card.Weight;
						break;
					}
				}
				res_info.weights_value = base_weight * POW_15_4 + rest_card.IgnoreAnyWeight();
				break;// 如果有三个癞子，可能会有额外的炸弹
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	public bool IsThree(CardList card_list, out Is_NormalResInfo res_info, int s_len = 5)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == s_len)
		{
			foreach (var combination in CheckThree(card_list))
			{
				var rest_card_list = CardList.FilterRemoveCards(card_list, combination);
				int base_weight = 0;
				foreach (var card in combination)
				{
					if (!card.IsAny)
					{
						base_weight = card.Weight;
						break;
					}
				}
				var rest_cards = rest_card_list.OrderBy(delegate (Card a)
					{// 从小到大排序
						return a.Weight;
					}).ToArray();
				res_info.weights_value = base_weight * POW_15_4;
				for (int i = 0; i < rest_cards.Length; i++)
				{
					res_info.weights_value += rest_cards[i].IgnoreAnyWeight() * POW_15[i];
				}
				break;// 如果有两个癞子，可能会有额外的三条
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	public bool IsTwo(CardList card_list, out Is_NormalResInfo res_info, int s_len = 5)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == s_len)
		{
			foreach (var combination in CheckTwo(card_list))
			{
				var rest_card_list = CardList.FilterRemoveCards(card_list, combination);
				int base_weight = 0;
				foreach (var card in combination)
				{
					if (!card.IsAny)
					{
						base_weight = card.Weight;
						break;
					}
				}
				var rest_cards = rest_card_list.OrderBy(delegate (Card a)
					{// 从小到大排序
						return a.Weight;
					}).ToArray();
				res_info.weights_value = base_weight * POW_15_4;
				for (int i = 0; i < rest_cards.Length; i++)
				{
					res_info.weights_value += rest_cards[i].IgnoreAnyWeight() * POW_15[i];
				}
				break;// 如果有癞子，可能会有第二对
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	/// <summary>
	/// 获取两对的组合可能，取出后，前两张的权重大于后两张
	/// </summary>
	/// <param name="card_list"></param>
	/// <returns></returns>
	public IEnumerable<Card[]> CheckTwoPair(CardList card_list)
	{
		var two_cards_list = CheckSameNumber(card_list, 2).ToArray();
		if (two_cards_list.Length > 0)
		{
			for (int i = 0, i_len = two_cards_list.Length - 1; i < i_len; i++)
			{
				var two_cards_1 = two_cards_list[i];
				// 监测进行组合的两组对子避免出现炸弹
				var card_number_set = new List<int>();

				var two_cards_1_weight = two_cards_1[0].Weight;
				if (two_cards_1[0].IsAny)
				{
					// 不可能出现两癞子的对子，两癞子就可定是出三条以上了
					two_cards_1_weight = two_cards_1[1].IgnoreAnyWeight();
				}
				card_number_set.Add(two_cards_1_weight);
				for (int j = i + 1; j < two_cards_list.Length; j++)
				{// 从i+1取确保前面的对子肯定大于者，方便计算权重
					var two_cards_2 = two_cards_list[j];
					var two_cards_2_weight = two_cards_2[0].Weight;
					if (two_cards_2[0].IsAny)
					{
						two_cards_2_weight = two_cards_2[1].IgnoreAnyWeight();
						// 全癞子有且只可能出现一次，所以这里就不用监测是否这个对子也是癞子
					}
					if (card_number_set.Contains(two_cards_2_weight))
					{// 避免出现炸弹组合
						continue;
					}
					if (two_cards_1.Where(card => two_cards_2.Contains(card)).Count() > 0)
					{//两个对子不能存在重复使用的牌
						continue;
					}
					yield return two_cards_1.Concat(two_cards_2).ToArray();
				}
			}
		}
	}
	public bool IsTwoPair(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in CheckTwoPair(card_list))
			{
				// 第一个对子的权重，不可能再出现癞子了，都可以三条了！
				var tow_card_1_weight = combination[0].IsAny
					? combination[1].Weight
					: combination[0].Weight;
				// 第二对子的权重，不可能再出现癞子了，都可以炸弹了！
				var tow_card_2_weight = combination[2].IsAny
					? combination[3].Weight
					: combination[2].Weight;

				var rest_card = CardList.FilterRemoveCards(card_list, combination)[0];

				res_info.weights_value = tow_card_1_weight * POW_15_4
					+ tow_card_2_weight * POW_15_2
					+ rest_card.IgnoreAnyWeight() * POW_15_0;
				break;// 如果有癞子可能出现额外的两对
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	public IEnumerable<Card[]> CheckFullHouse(CardList card_list)
	{
		foreach (var three_cards in CheckThree(card_list))
		{
			var three_card_weight = three_cards[0].IgnoreAnyWeight(three_cards[1].IgnoreAnyWeight(three_cards[2].IgnoreAnyWeight()));
			var rest_cards = CardList.FilterRemoveCards(card_list, three_cards);
			foreach (var two_cards in CheckTwo(rest_cards))
			{
				var two_card_weight = two_cards[0].IgnoreAnyWeight(two_cards[1].IgnoreAnyWeight());
				if (two_card_weight == three_card_weight)// 避免出现五炸
				{
					continue;
				}
				yield return three_cards.Concat(two_cards).ToArray();
			}
		}
	}
	public bool IsFullHouse(CardList card_list, out Is_NormalResInfo res_info)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count == 5)
		{
			foreach (var combination in CheckFullHouse(card_list))
			{
				var three_card_weight = combination[0].IgnoreAnyWeight(combination[1].IgnoreAnyWeight(combination[2].IgnoreAnyWeight()));
				var two_card_weight = combination[3].IgnoreAnyWeight(combination[4].IgnoreAnyWeight());
				res_info.weights_value = three_card_weight * POW_15_4 + two_card_weight * POW_15_1;
				break;
			}
			return res_info.weights_value > 0;
		}
		return false;
	}
	public bool IsAny(CardList card_list, out Is_NormalResInfo res_info, int len = 5)
	{
		res_info = new Is_NormalResInfo();
		if (card_list.Count != len)
		{
			return false;
		}
		var sorted_card_list = card_list.OrderBy(card => card.Weight);// 从小到大排序
		var i = 0;
		foreach (var card in sorted_card_list)
		{
			res_info.weights_value += card.Weight * POW_15[i + 5 - len];
			i += 1;
		}
		return true;
	}

	/*特殊牌监测，不可有癞子*/
	/// <summary>
	/// 至尊青龙
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsKingLong(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		Is_SpecialResInfo one_long_res_info;
		if (IsOneLong(card_list, out one_long_res_info) && IsOneLongAndKingLong(card_list))
		{
			res_info.combinations = one_long_res_info.combinations;
			return true;
		}
		return false;
	}
	/// <summary>
	/// 一条龙后再检测是否至尊青龙
	/// </summary>
	/// <param name="card_list"></param>
	/// <returns></returns>
	bool IsOneLongAndKingLong(CardList card_list)
	{
		var color_type = card_list[0].ValueOfType();
		if (card_list.All(card => card.ValueOfType() == color_type))
		{
			return true;
		}
		return false;
	}
	/// <summary>
	/// 一条龙
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsOneLong(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var sorted_card_list = card_list.OrderBy(card => card.Weight).ToArray();
			for (int i = 0; i < 13; i++)
			{
				if (sorted_card_list[i].Weight != i + 2)
				{
					return false;
				}
			}
			var combination = new Combination(sorted_card_list);
			res_info.combinations.Add(combination);
			return true;
		}
		return false;
	}
	/// <summary>
	/// 六对半
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsSixPair(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var rest_card_list = card_list;
			for (int i = 0; i < 6; i++)
			{
				var check = CheckTwo(rest_card_list);
				if (!check.Any())
				{
					return false;
				}
				var two_cards = check.First();
				rest_card_list = CardList.FilterRemoveCards(rest_card_list, two_cards);
			}
			// 成功取出6个对子
			return true;
		}
		return false;
	}
	/// <summary>
	/// 三同花
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsThreeFlush(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var type_map = new Dictionary<int, List<Card>>();
			foreach (var card in card_list)
			{
				var card_type = card.ValueOfType();
				List<Card> same_type_card_list;
				if (!type_map.TryGetValue(card_type, out same_type_card_list))
				{
					same_type_card_list = new List<Card>();
					type_map.Add(card_type, same_type_card_list);
					if (type_map.Count > 3)// 超过三种花色
					{
						return false;
					}
				}
				same_type_card_list.Add(card);
			}
			switch (type_map.Count)
			{
			case 1:
				var sorted_card_list = card_list.OrderBy(card => card.Weight).ToArray();
				res_info.combinations.Add(new Combination(sorted_card_list));
				break;
			case 2:
				{

					var cards_list = type_map.ToArray().Select(KV_pair => KV_pair.Value).OrderBy(same_type_card_list => same_type_card_list.Count).ToArray();
					var cards_1 = cards_list[0];
					var cards_2 = cards_list[1];
					if (cards_1.Count == 3)
					{
						var cards_5_5 = cards_2.OrderBy(card => card.Weight);
						res_info.combinations.Add(new Combination(
							cards_1.ToArray(),
							cards_5_5.Take(5).ToArray(),
							cards_5_5.Skip(5).Take(5).ToArray()
						));
						return true;
					}
					else if (cards_1.Count == 5)
					{
						var cards_3_5 = cards_2.OrderBy(card => card.Weight);
						var cards_mid_or_btm_1 = cards_1.ToArray();
						var cards_mid_or_btm_2 = cards_3_5.Skip(3).Take(5).ToArray();
						Is_NormalResInfo _cards_info_1;
						Is_NormalResInfo _cards_info_2;
						IsFlush(new CardList(cards_mid_or_btm_1), out _cards_info_1);
						IsFlush(new CardList(cards_mid_or_btm_2), out _cards_info_2);
						if (_cards_info_1.weights_value > _cards_info_2.weights_value)
						{
							res_info.combinations.Add(new Combination(
								cards_3_5.Take(3).ToArray(),
								cards_mid_or_btm_2,
								cards_mid_or_btm_1
							));
						}
						else
						{
							res_info.combinations.Add(new Combination(
								cards_3_5.Take(3).ToArray(),
								cards_mid_or_btm_1,
								cards_mid_or_btm_2
							));
						}
						return true;
					}
				}
				return false;
			case 3:
				{
					var cards_list = type_map.ToArray().Select(KV_pair => KV_pair.Value).OrderBy(same_type_card_list => same_type_card_list.Count).ToArray();
					var cards_3 = cards_list[0];
					var cards_5_1 = cards_list[1];
					var cards_5_2 = cards_list[2];
					if (cards_3.Count == 3 && cards_5_1.Count == 5)
					{
						Is_NormalResInfo _cards_info_1;
						Is_NormalResInfo _cards_info_2;
						IsFlush(new CardList(cards_5_1), out _cards_info_1);
						IsFlush(new CardList(cards_5_2), out _cards_info_2);
						if (_cards_info_1.weights_value > _cards_info_2.weights_value)
						{
							res_info.combinations.Add(new Combination(
								cards_3.ToArray(),
								cards_5_2.ToArray(),
								cards_5_1.ToArray()
							));
						}
						else
						{
							res_info.combinations.Add(new Combination(
								cards_3.ToArray(),
								cards_5_1.ToArray(),
								cards_5_2.ToArray()
							));
						}
						return true;
					}
				}
				return false;
			default:
				break;
			}
			return true;
		}
		return false;
	}
	/// <summary>
	/// 全小
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsAllSmall(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard() && card_list.All(card => card.Weight <= 8))
		{
			return true;
			//CheckFour(card_list).Count() == 0 && CheckStraightFlush(card_list).Count() == 0;
		}
		return false;
	}
	/// <summary>
	/// 全大
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsAllBig(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard() && card_list.All(card => card.Weight >= 8))
		{
			return true;
			//CheckFour(card_list).Count() == 0 && CheckStraightFlush(card_list).Count() == 0;
		}
		return false;
	}
	public IEnumerable<Combination> CheckThreeStraight(CardList card_list)
	{
		if (card_list.Count != 13 || card_list.HasLaiziCard())
			yield break;
		//if (CheckFour(card_list).Count() != 0)
		//    yield break;
		//if (CheckStraightFlush(card_list).Count() != 0)
		//    yield break;

		var _sort_methods = new int[] { 1, -1 };
		foreach (var sort_method in _sort_methods)
		{
			var TOP_KEY = "top";
			var MID_KEY = sort_method == 1 ? "mid" : "btm";
			var BTM_KEY = sort_method == 1 ? "btm" : "mid";
			// 先取两个五顺，再取一个三顺
			// 两种排序方式，一种是根据权值排序，234~JQKA，一种是根据数值排序，A23~JQK
			// WVMap提供了这两种排序方式的混合体，就是A2~KA
			// 不过不同的是，需要进行两次判定方案，就是从小到大（A2345）与从大到小（AKQJT）获取顺子

			//权值排序
			var weight_card_map = new WVMap<List<Card>>();
			foreach (var card in card_list)
			{
				var card_weight = card.Weight;
				List<Card> same_weight_cards;
				if (!weight_card_map.TryGetValue(card_weight, out same_weight_cards))
				{
					same_weight_cards = new List<Card>();
					weight_card_map.Set(card_weight, same_weight_cards);
				}
				same_weight_cards.Add(card);
			}
			;
			if (weight_card_map.Keys.Count < 5)
			{
				yield break;
			}
			var card_weight_list = weight_card_map.Keys.ToList();
			card_weight_list.Sort((_a, _b) => (_b - _a) * sort_method);

			foreach (var item in weight_card_map)
			{
				// 对花色进行排序，花色的顺序不能错，因为下面取元素的时候，是拿同值牌组中的最后一个，所以花色顺序如果错了话，同花顺会拼接失败
				item.Value.Sort((Card card_a, Card card_b) => card_a.ValueOfType() - card_b.ValueOfType());
			}

			foreach (var type_and_num_list in new TypeAndNum[][]{
				new TypeAndNum[] { new TypeAndNum(BTM_KEY, 5), new TypeAndNum(MID_KEY, 5), new TypeAndNum(TOP_KEY, 3) },
				new TypeAndNum[] { new TypeAndNum(BTM_KEY, 5), new TypeAndNum(TOP_KEY, 3), new TypeAndNum(MID_KEY, 5) },
				new TypeAndNum[] { new TypeAndNum(TOP_KEY, 3), new TypeAndNum(BTM_KEY, 5), new TypeAndNum(MID_KEY, 5) },
			})
			{
				// 复制map
				var weight_card_map_clone = new WVMap<List<Card>>();
				foreach (var item in weight_card_map)
				{
					weight_card_map_clone.Set(item.Key, item.Value.ToList());
				}
				var _res_cards = new Dictionary<string, List<Card>>();
				if (type_and_num_list.All(type_and_num =>
					{
						var type = type_and_num.type;
						var num = type_and_num.num;
						List<Card> straight_list;
						if (!_res_cards.TryGetValue(type, out straight_list))
						{
							straight_list = new List<Card>();
							_res_cards.Add(type, straight_list);
						}
						var _straight_weight_list = GetStraightFromList(weight_card_map_clone.Keys.ToArray(), sort_method, num);
						if (_straight_weight_list != null)
						{
							foreach (var card_weight in _straight_weight_list)
							{
								var cards = weight_card_map_clone[card_weight];
								if (cards.Count == 0)
								{
									return false;
								}
								// pop
								var last_card = cards.Last();
								cards.RemoveAt(cards.Count - 1);

								straight_list.Add(last_card);
								if (cards.Count == 0)
								{
									weight_card_map_clone.Delete(card_weight);
								}
							}
							return true;
						}
						return false;
					}))
				{
					yield return new Combination(
						_res_cards["top"].ToArray(),
						_res_cards["mid"].ToArray(),
						_res_cards["btm"].ToArray()
					);
				}
			}
		}
	}
	/// <summary>
	/// 三顺子
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsThreeStraight(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		foreach (var combination in CheckThreeStraight(card_list))
		{
			res_info.combinations.Add(combination);
			return true;
		}
		return false;
	}
	/// <summary>
	/// 四套三条
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsFourThree(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var rest_card_list = card_list;
			for (int i = 0; i < 4; i++)
			{
				var check = CheckThree(rest_card_list);
				if (!check.Any())
				{
					return false;
				}
				var three_cards = check.First();
				rest_card_list = CardList.FilterRemoveCards(rest_card_list, three_cards);
			}
			// 能拿出四个三条，通过
			return true;
		}
		return false;
	}
	/// <summary>
	/// 三炸弹（三分天下）
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsThreeFour(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var rest_card_list = card_list;
			for (int i = 0; i < 3; i++)
			{
				var check = CheckFour(rest_card_list);
				if (!check.Any())
				{
					return false;
				}
				var four_cards = check.First();
				rest_card_list = CardList.FilterRemoveCards(rest_card_list, four_cards);
			}
			// 能拿出三个炸弹，通过
			return true;
		}
		return false;
	}
	/// <summary>
	/// 三皇五帝
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsTwoFiveAndOneThree(CardList card_list, out Is_SpecialResInfo res_info)
	{

		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var rest_card_list = card_list;
			for (int i = 0; i < 2; i++)
			{
				var check = CheckFive(rest_card_list);
				if (!check.Any())
				{
					return false;
				}
				var five_cards = check.First();
				rest_card_list = CardList.FilterRemoveCards(rest_card_list, five_cards);
			}
			var check_2 = CheckThree(rest_card_list);
			if (!check_2.Any())
			{
				return false;
			}
			var three_cards = check_2.First();
			// 能拿出三皇五帝，过关
			return true;
		}
		return false;
	}
	/// <summary>
	/// 十二皇族
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsTwelveRoyal(CardList card_list, out Is_SpecialResInfo res_info)
	{

		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard() && card_list.All(card => card.Weight >= 11 && card.Weight <= 14))
		{
			return true;
		}
		return false;
	}
	/// <summary>
	/// 五张一样
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsOneFive(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			if (CheckSameNumber(card_list, 5).Any())
			{
				return true;
			}
		}
		return false;
	}
	/// <summary>
	/// 六六大顺
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsOneSix(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			if (CheckSameNumber(card_list, 6).Any())
			{
				return true;
			}
		}
		return false;
	}
	/// <summary>
	/// 五对三冲
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>FlushStraight
	public bool IsFivePairAndOneThree(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var rest_card_list = card_list;
			var card_val_map = new HashSet<int>();
			// 先找出6个对子
			for (int i = 0; i < 6; i++)
			{
				var check = CheckTwo(rest_card_list);
				if (!check.Any())
				{
					return false;
				}
				var two_cards = check.First();
				card_val_map.Add(two_cards[0].ValueOf());
				rest_card_list = CardList.FilterRemoveCards(rest_card_list, two_cards);
			}
			if (card_val_map.Contains(rest_card_list[0].ValueOf()))
			{
				// 成功取出6个对子，并且余牌能与这些对子中的一个配成一个三条
				return true;
			}
		}
		return false;
	}
	/// <summary>
	/// 凑一色
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsSameColor(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13 && !card_list.HasLaiziCard())
		{
			var color = card_list[0].Color;
			if (card_list.All(card => card.Color == color))
			{
				return true;
			}
		}
		return false;
	}
	/// <summary>
	/// 三同花顺
	/// </summary>
	/// <param name="card_list"></param>
	/// <param name="res_info"></param>
	/// <returns></returns>
	public bool IsThreeStraightFlush(CardList card_list, out Is_SpecialResInfo res_info)
	{
		res_info = new Is_SpecialResInfo();
		foreach (var combination in CheckThreeStraight(card_list))
		{
			var top_type = combination.Top[0].Type;
			var mid_type = combination.Mid[0].Type;
			var btm_type = combination.Btm[0].Type;
			if (
				combination.Top.All(card => card.Type == top_type)
				&&
				combination.Mid.All(card => card.Type == mid_type)
				&&
				combination.Btm.All(card => card.Type == btm_type)
			)
			{
				res_info.combinations.Add(combination);
				return true;
			}
		}
		return false;
	}
	struct TypeAndNum
	{
		public string type;
		public int num;
		public TypeAndNum(string type, int num)
		{
			this.type = type;
			this.num = num;
		}
	}
	static public int[] GetStraightFromList(int[] list, int sort_type = 1, int res_len = 5)
	{
		Array.Sort(list, (a, b) => (b - a) * sort_type);
		var list_len = list.Length;
		var _q = -1;
		if (list_len > res_len)
		{
			for (int i = 0, max_i = list_len - res_len; i <= max_i; i += 1)
			{
				var _w = -1;
				if (
					list
					.Skip(i).Take(res_len - 1)
					.All(item =>
						{
							_w += 1;
							return item - list[_w + 1 + i] == sort_type;
						})
				)
				{
					return list.Skip(i).Take(res_len).ToArray();
				}
			}
		}
		else if (list_len == res_len && list.Take(res_len - 1).All(item =>
			{
				_q += 1;
				return item - list[_q + 1] == sort_type;
			}))
		{
			return list.ToArray();
		}
		return null;
	}
	public bool IsSpeical(CardList card_list, out Is_SpecialResInfo res_info)
	{
		var types = new string[] {
			"KingLong", //至尊青龙
			"OneLong", //一条龙
			"SixPair", //六对半
			"ThreeFlush", //三同花
			"AllSmall", //全小
			"AllBig", //全大
			"ThreeStraight", //三顺子
			"FourThree", //四套三条
			"ThreeFour", //三分天下
			"TwoFiveAndOneThree", //三皇五帝
			"TwelveRoyal", //十二皇族
			"OneFive", //五张一样
			"OneSix", //六六大顺
			"FivePairAndOneThree", //五对三冲
			"SameColor", //凑一色
			"ThreeStraightFlush",//三同花顺
		};
		return IsSpeical(card_list, out res_info, types);
	}

	public bool IsSpeical(CardList card_list, out Is_SpecialResInfo res_info, string[] types)
	{
		res_info = new Is_SpecialResInfo();
		if (card_list.Count == 13)
		{
			// 一条龙或者至尊青龙
			if (IsOneLong(card_list, out res_info))
			{
				if (IsOneLongAndKingLong(card_list))
					res_info.type = "KingLong";
				else
					res_info.type = "OneLong";
				return true;
			}
			foreach (var type in types)
			{
				bool is_res = false;
				switch (type)
				{
				case "KingLong":
					is_res = IsKingLong(card_list, out res_info);
					break;
				case "OneLong":
					is_res = IsOneLong(card_list, out res_info);
					break;
				case "SixPair":
					is_res = IsSixPair(card_list, out res_info);
					break;
				case "ThreeFlush":
					is_res = IsThreeFlush(card_list, out res_info);
					break;
				case "AllSmall":
					is_res = IsAllSmall(card_list, out res_info);
					break;
				case "AllBig":
					is_res = IsAllBig(card_list, out res_info);
					break;
				case "ThreeStraight":
					is_res = IsThreeStraight(card_list, out res_info);
					break;
				case "FourThree":
					is_res = IsFourThree(card_list, out res_info);
					break;
				case "ThreeFour":
					is_res = IsThreeFour(card_list, out res_info);
					break;
				case "TwoFiveAndOneThree":
					is_res = IsTwoFiveAndOneThree(card_list, out res_info);
					break;
				case "TwelveRoyal":
					is_res = IsTwelveRoyal(card_list, out res_info);
					break;
				case "OneFive":
					is_res = IsOneFive(card_list, out res_info);
					break;
				case "OneSix":
					is_res = IsOneSix(card_list, out res_info);
					break;
				case "FivePairAndOneThree":
					is_res = IsFivePairAndOneThree(card_list, out res_info);
					break;
				case "SameColor":
					is_res = IsSameColor(card_list, out res_info);
					break;
				case "ThreeStraightFlush":
					is_res = IsThreeStraightFlush(card_list, out res_info);
					break;
				}
				if (is_res)
				{
					res_info.type = type;
					return true;
				}
			}
		}
		return false;
	}
}
class WVMap<TValue> : Dictionary<int, TValue>
{
	public WVMap() { }
	public WVMap(Dictionary<int, TValue> dic)
	{
		foreach (var kv in dic)
		{
			this.Add(kv.Key, kv.Value);
		}
	}
	public void Set(int k, TValue v)
	{
		if (k == 14 || k == 1)
		{
			base[1] = base[14] = v;
		}
		else
		{
			base[k] = v;
		}
	}
	public bool Has(int k)
	{
		if (k == 14 || k == 1)
		{
			return base.ContainsKey(1) || base.ContainsKey(14);
		}
		else
		{
			return base.ContainsKey(k);
		}
	}
	public void Delete(int k)
	{
		if (k == 14 || k == 1)
		{
			base.Remove(1);
			base.Remove(14);
		}
		else
		{
			base.Remove(k);
		}
	}
}